package com.settlement.system.csv;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.settlement.system.common.enums.ExcelFileType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanMap;

import java.io.*;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * csv文件写入
 *
 * @author Ximi
 * @since 2020/08/31
 */
@Slf4j
public class SingleCsvWrite extends AbstractCsvWrite {

    private Integer size;
    /**
     * 文件写入流
     */
    private BufferedWriter bufferedWriter;
    private ExcelWriter excelWriter;
    private WriteSheet writeSheet;
    private File file;
    private Integer model;

    private final String stringInDefaultFileName = "(默认)";

    public SingleCsvWrite(ExcelFileType fileType,
                          Class headClass,
                          Headers headers,
                          String fileName,
                          boolean customized,
                          boolean ifMapData,
                          List<List<String>> xlsxHeaderList,
                          Map<Integer, Integer> columnWidthMap
    ) throws IOException {
        super(fileType, headClass, headers, fileName, customized, ifMapData, xlsxHeaderList, columnWidthMap);
    }

    @Override
    public void initFile() throws IOException {
        try {
            size = 0;
            if (fileType.equals(ExcelFileType.XLSX)) {
                file = new File(fileName + ".xlsx");
                ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(fileName + ".xlsx");
                if (ifMapData) {
                    excelWriter = excelWriterBuilder.head(xlsxHeaderList).build();
                } else {
                    // XLSX格式，自定义导出且不是默认的，才需要以这种方式写头部
                    if (customized && !fileName.contains(stringInDefaultFileName)) {
                        excelWriter = excelWriterBuilder.head(getXlsxHeaderList(headers)).build();
                    } else {
                        excelWriter = excelWriterBuilder.head(headClass).build();
                    }
                }
                size++;
                String sheetName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
                writeSheet = EasyExcel.writerSheet(sheetName).build();
                if (columnWidthMap != null) {
                    /*writeSheet.setColumnWidthMap(columnWidthMap);*/
                }
            } else {
                file = new File(fileName + ".csv");

                FileOutputStream fos = new FileOutputStream(file);
                // 写入bom头
                byte[] uft8bom = {(byte) 0xef, (byte) 0xbb, (byte) 0xbf};
                fos.write(uft8bom);

                bufferedWriter = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"), 1024);
                writeHeaders(headers);
            }
        } catch (Exception ex) {
            log.error("文件写入流异常", ex);
            throw ex;
        }
    }

    /**
     * 写入文件头部
     *
     * @param headers
     * @throws IOException
     */
    private void writeHeaders(Headers headers) throws IOException {
        StringBuilder builder = new StringBuilder();
        for (int index = 0; index < headers.getTitles().size(); index++) {
            if (index != 0) {
                builder.append(",");
            }
            builder.append(headers.getTitles().get(index).trim());
        }
        bufferedWriter.write(builder.toString());
        size++;
    }

    List<List<String>> getXlsxHeaderList(Headers headers) {
        List<List<String>> headList = new ArrayList();
        List<String> headerList = new ArrayList();
        for (int index = 0; index < headers.getTitles().size(); index++) {
            headerList = new ArrayList();
            headerList.add(headers.getTitles().get(index).trim());
            headList.add(headerList);
        }
        return headList;
    }

    @Override
    public void write(Object data) throws Exception {
        if (fileType.equals(ExcelFileType.XLSX)) {
            // XLSX格式，自定义导出且不是默认的，才需要以这种方式数据
            if (customized && !fileName.contains(stringInDefaultFileName)) {
                Map<String, Object> map = new BeanMap(data);
                try {
                    if (customized) {
                        List<List<Object>> list = new ArrayList<>();

                        List<Object> datas = new ArrayList<>();
                        for (int index = 0; index < headers.getFields().size(); index++) {
                            Object value = map.get(headers.getFields().get(index));
                            if (value instanceof Timestamp) {
                                datas.add(new Date(((Timestamp) value).getTime()));
                            } else {
                                datas.add(value);
                            }
                        }
                        list.add(datas);
                        excelWriter.write(list, writeSheet);
                        size++;
                    } else {
                        Object o = headClass.newInstance();
                        for (int index = 0; index < headers.getFields().size(); index++) {
                            String fieldName = headers.getFields().get(index);
                            Object value = map.get(headers.getFields().get(index));
                            Field declaredField = o.getClass().getDeclaredField(fieldName);
                            declaredField.setAccessible(true);
                            declaredField.set(o, value);
                        }
                        List datas = new ArrayList();
                        datas.add(o);
                        excelWriter.write(datas, writeSheet);
                        size++;
                    }
                } catch (Exception e) {
                    log.error("文件写入异常", e);
                    throw e;
                }
            } else {
                if (ifMapData) {
                    Map<String, Object> map = (Map<String, Object>) data;
                    List<List<Object>> list = new ArrayList<>();

                    List<Object> datas = new ArrayList<>();
                    for (int index = 0; index < headers.getFields().size(); index++) {
                        Object value = map.get(headers.getFields().get(index));
                        if (value instanceof Timestamp) {
                            datas.add(new Date(((Timestamp) value).getTime()));
                        } else {
                            datas.add(value);
                        }
                    }
                    list.add(datas);
                    excelWriter.write(list, writeSheet);
                } else {
                    List datas = new ArrayList();
                    datas.add(data);
                    excelWriter.write(datas, writeSheet);
                }
                size++;
            }
        } else if (fileType.equals(ExcelFileType.CSV)) {
            bufferedWriter.newLine();
            StringBuilder builder = new StringBuilder();
            Map<String, Object> map = null;
            if (ifMapData) {
                map = (Map<String, Object>) data;
            } else {
                map = new BeanMap(data);
            }

            for (int index = 0; index < headers.getFields().size(); index++) {
                Object value = map.get(headers.getFields().get(index));
                if (index > 0) {
                    builder.append(",");
                }

                try {
                    Converter converter = headers.getHeader(index).getConverter();
                    if (converter != null && value != null) {
                        CellData cellData = converter.convertToExcelData(value, null, null);
                        value = getValue(cellData);
                    }
                } catch (Exception ex) {
                    log.error("converter 转化异常", ex);
                }

                if (value != null) {

                    if (value.toString().contains(",")) {
                        value = "\"" + value + "\"";
                    }
                    if (value.toString().contains("\n")) {
                        value = value.toString().replaceAll("\n", " ");
                    }

                    builder.append(value.toString());
                }
            }

            bufferedWriter.write(builder.toString());
            size++;
        }
    }

    private Object getValue(CellData cellData) {

        if (cellData == null || cellData.getType() == null) {
            return null;
        }
        if (cellData.getType().equals(CellDataTypeEnum.STRING)) {
            return cellData.getStringValue();
        }
        return cellData.getStringValue();
    }

    @Override
    public void close() {
        if (fileType.equals(ExcelFileType.CSV)) {
            if (bufferedWriter != null) {
                try {
                    bufferedWriter.flush();
                    bufferedWriter.close();
                    bufferedWriter = null;
                } catch (IOException e) {
                    log.error("关闭文件流异常", e);
                }
            }
        } else if (fileType.equals(ExcelFileType.XLSX)) {
            if (excelWriter != null) {
                excelWriter.finish();
                excelWriter = null;
            }
        }
    }

    @Override
    public File getFile() throws IOException {
        close();
        return file;
    }

    public Integer size() {
        return size;
    }
}
